#include <VCL\VCL.h>
#include <EIC.h>
#pragma hdrstop

#include "dmCpta32.h"
#include "Compta.hrc"
#include "CEditCpt.h"

 #include <Dir.h>
 #include <ErrNo.h>

 #include "CptaAss.h"
#include <map>


#pragma link "Compta"
#pragma link "CTable"
#pragma link "CTable"
#pragma link "compta"
#pragma link "ctable"
#pragma resource "*.dfm"
#define MODULE_TAG 1004

#ifdef _DEBUG
 #pragma message( "*** Version DEBUG des composants ***" )
#endif                                                             


//*******************************//
// Variables publiques de la DLL //
//*******************************//
TdmCompta* _pdmCompta = NULL;
TFileStream *FichierDonneesExtra = 0;


//*******************************//
// Variables statiques de la DLL //
//*******************************//
TStringList *TdmCompta::ComptesAvecMauvaiseClasse;
static TDateTime    _dtImputation;     // Caractéristiques d'une écriture
static int          _nIDEcriture;      // ID de l'écriture à créer/mettre à jour par un module émulé
static unsigned int _eRaoIDEcriture;   // ID perso du module émulé émetteur d'une écriture
static AnsiString   _strEcrJournal;    // Journal dans lequel une écriture (émulée ou importée) doit être émise
static bool         _bPieceConst;      // TRUE si la pièce de l'écriture est constante
static AnsiString   _strPieceConst;    // Pièce constante qui justifie l'écriture (émulée ou importée) qui doit être émise (ignorée si !_bPieceConst)
static bool         _bContrepasser;    // TRUE s'il faut contrepasser l'écriture émulée
static AnsiString   _strEmulRacine;    // Racine en cours d'énumération (pour les modules émulés)
static unsigned long _IDEcriture;      // IDEcriture en cours d'énumération


#ifdef _DEBUG
 #define ASSERT_PC()                    AssertPC( __LINE__)
 #define ASSERT_HI()                    AssertHI( __LINE__)

 static void __fastcall AssertPC( unsigned long ulLineNb) { if( !( _pdmCompta && _pdmCompta->tblPC && _pdmCompta->tblPC->Active)) Assert( ulLineNb, __FILE__);}
 static void __fastcall AssertHI( unsigned long ulLineNb) { if( !( _pdmCompta && _pdmCompta->tblHI && _pdmCompta->tblHI->Active) && !( _pdmCompta && _pdmCompta->tbl2H && _pdmCompta->tbl2H->Active) ) Assert( ulLineNb, __FILE__);}
#else
 #define ASSERT_PC()
 #define ASSERT_HI()
#endif



void __fastcall OpenTables( bool bPourImport)
{
 if( _pdmCompta == NULL)
   _pdmCompta = new TdmCompta();
 _pdmCompta->OpenTablesCompta( bPourImport);
}



void __fastcall CloseTables()
{
 if( _pdmCompta)
 {
   _pdmCompta->CloseTablesCompta();
   if( _pdmCompta->NbOpenCount <= 0)
   {
     delete _pdmCompta;
     _pdmCompta = NULL;
   }
 }
}




/******************************************************************************\
************* TdmCompta::TdmCompta() *******************************************
********************************************************************************
*                                                                              *
*    Constructeur du module de données de la compta. Ce module contient toutes *
* les tables de compta du dossier actif (accédées via les alias EIC_*).        *
*                                                                              *
* Paramètres :                                                                 *
*   pOwner : Objet possesseur du module de données.                            *
\******************************************************************************/
__fastcall TdmCompta::TdmCompta() : TDataModule(0)
{
  m_nNbOpenTables = 0; // Aucune table n'est encore ouverte
  ComptesAvecMauvaiseClasse = 0;

  ASSERT( !tblPC->Active);
  ASSERT( !tblJnx->Active);
  ASSERT( !tblHI->Active);
  ASSERT( !tbl2H->Active);
  ASSERT( !tblHITmp->Active);
}



__fastcall TdmCompta::~TdmCompta()
{
  delete ComptesAvecMauvaiseClasse;
  ComptesAvecMauvaiseClasse = 0;
}


/******************************************************************************\
************* TdmCompta::OpenTablesCompta() ************************************
********************************************************************************
*                                                                              *
*    Cette procédure permet d'ouvrir toutes les tables du module de données.   *
*                                                                              *
* Paramètres :                                                                 *
*   bPourImport : TRUE s'il faut ouvrir les tables en vue d'un import (dans ce *
*                 cas, le HI et le PC sont ouverts en exclusif et le résultat  *
*                 comptable n'est pas mis à jour lors des mouvements du PC).   *
\******************************************************************************/
void __fastcall TdmCompta::OpenTablesCompta( bool bPourImport)
{
 EICTag tag;

 ASSERT( m_nNbOpenTables >= 0);


 ///////////////////////////////////////
 // Restauration des index par défaut //
 ///////////////////////////////////////
 if( m_nNbOpenTables == 0)
 { // Ouverture explicite de toutes les tables de compta
   try {
     tblHI->IndexName = EmptyStr;
     tag = TAG( 10); tblPC->Exclusive = bPourImport; /*tblPC->MAJResultat = bPourImport^true;*/ tblPC->Open();
     tag = TAG( 11);                                 tblJnx->Open();
     tag = TAG( 12); tblHI->Exclusive = bPourImport; tblHI->Open();  tbl2H->Open();
     tag = TAG( 13);                                 tblHITmp->Open();
   } catch( Exception& e) {
     tblPC->Open();
     tblJnx->Open();
     tblHI->Open();
     tbl2H->Open();     
     tblHITmp->Open();
     throw EUser( tag, e);
   }
 }
 else
 { // Si les tables sont déjà ouvertes, on ne peut pas les réouvrir pour un import
   ASSERT( !bPourImport);
 }
 m_nNbOpenTables++;

 ASSERT( m_nNbOpenTables > 0);
} /* TdmCompta::OpenTablesCompta() */


/******************************************************************************\
************* TdmCompta::CloseTablesCompta() ***********************************
********************************************************************************
*                                                                              *
*    Cette procédure permet de fermer toutes les tables du module de données.  *
*                                                                              *
* ATTENTION : Le fermeture effective des tables n'est réalisée que s'il y a eu *
*             autant d'appels à CloseTables qu'a OpenTables.                   *
\******************************************************************************/
void __fastcall TdmCompta::CloseTablesCompta()
{
 ASSERT(m_nNbOpenTables > 0);

 m_nNbOpenTables--;
 if( m_nNbOpenTables <= 0)
 { // Fermeture explicite de toutes les tables de compta
   try {
     tblHITmp->Close();
     tblHI->Close();
     tbl2H->Close();
     tblJnx->Close();
     tblPC->Close();
   } catch( Exception& e) {
     EICDisplayException( TAG( 20), e);
     throw;
   }
 }

 ASSERT( m_nNbOpenTables >= 0);
}



//*************************************************************************//
// Prototypes pour l'importation et les traitements de la version statique //
//*************************************************************************//
/*----------------------------------------------------------------------------*\
------------- EcritureOuvrir() -------------------------------------------------
--------------------------------------------------------------------------------
-                                                                              -
-    Cette fonction permet d'ouvrir la table temporaire du HI pour y créer une -
- écriture qui devra être ensuite validée (par EcritureValider) pour être      -
- insérée dans le HI du dossier.                                               -
-                                                                              -
- Paramètres :                                                                 -
-   strJnl         : Nom du journal dans lequel doit être passée l'écriture.   -
-   dateImputation : Date d'imputation de l'écriture.                          -
-   nIdEcriture    : ID de l'écriture à générer (Nouvelle ou modif existante)  -
-   bPieceConst    : TRUE si la pièce est constante sur l'écriture             -
-   strPieceConst  : Piece constante qui justifie l'écriture (si bPieceConst)  -
-   bContrepasser  : TRUE s'il faut contrepasser l'écriture                    -
-   eRAOIDEcriture : ID RAO de l'écriture (-1 si ce n'est pas une ecr RAO)     -
\*----------------------------------------------------------------------------*/
void __fastcall EcritureOuvrir( const AnsiString& strJnl,
                                const TDateTime&  dateImputation,
                                int               nIDEcriture,
                                bool              bPieceConst,
                                const AnsiString& strPieceConst,
                                bool              bContrepasser,
                                unsigned int      eRAOIDEcriture)
{
 ASSERT( !strJnl.IsEmpty());


 try {
   // Stockage des paramètres de l'écriture
   _strEcrJournal  = strJnl.Trim();  if( _strEcrJournal.IsEmpty()) EICThrowInvalidArgument( TAG( 30));
   _dtImputation   = dateImputation;
   _nIDEcriture    = nIDEcriture;
   _bPieceConst    = bPieceConst;
   _strPieceConst  = strPieceConst;
   _bContrepasser  = bContrepasser;
   _eRaoIDEcriture = eRAOIDEcriture;
   // Préparation de la table temporaire de l'écriture
   // Vidange de la table temporaire
   ASSERT( _pdmCompta->tblHITmp->State == dsBrowse);

   _pdmCompta->tblHITmp->EmptyTable();
 } catch( Exception& e) {
   throw EUser( TAG( 31), e);
 }
}



/*----------------------------------------------------------------------------*\
------------- EcritureInsereLigne() --------------------------------------------
--------------------------------------------------------------------------------
-                                                                              -
-    Cette fonction - exportée pour les assistants émulés - permet d'insérer   -
- dans la table temporaire du HI une ligne d'une écriture qui devra être       -
- ensuite validée (par EmulEcrValide) pour être insérée dans le HI du dossier. -
-                                                                              -
- Paramètres :                                                                 -
-   strCompte           : Compte à imputer                                     -
-   strLibelleOperation : Libellé de l'opération                               -
-   curMontant          : Montant de l'écriture                                -
-   bIsDebiteur         : TRUE si le montant est débiteur                      -
-   strPiece            : Code de la pièce de l'opération                      -
-   strLettrage         : Lettrage (d'origine) de l'opération                  -
-   pcRAOData           : Donnée de révision pour cette ligne d'écriture (NULL -
-                         pour aucne donnée de révision).                      -
\*----------------------------------------------------------------------------*/
void __fastcall EcritureInsereLigne( const AnsiString&     strCompte,
                                     const AnsiString&     strLibelleOperation,
                                     const Currency&       curMontant,
                                     bool                  bIsDebiteur,
                                     const AnsiString&     strPiece,
                                     const AnsiString&     strLettrage,
                                     const CPTA_HIRAODATA* pcRAOData)
{
  EICTag tag;
  AnsiString Libelle;

  ASSERT_HI();

 try {
    if (strCompte.IsEmpty()) tag = TAG( 220);
//    else if (strLibelleOperation.IsEmpty()) tag = TAG( 221);
    else if (curMontant == 0.0) tag = TAG( 222);
    else tag = 0; // Paramètres Ok
    if (tag)
      EICThrowInvalidArgument( tag);
    if (strLibelleOperation.IsEmpty())
      if (_pdmCompta->tblPC->AllerSurLeCompte(strCompte))
        Libelle = _pdmCompta->tblPCIntitule->AsString;
      else
        Libelle = "?";
    else
      Libelle = strLibelleOperation;
   _pdmCompta->tblHITmp->Append();

   _pdmCompta->tblHITmpCompte->AsString           = strCompte.Trim();
   _pdmCompta->tblHITmpLibelleOperation->AsString = Libelle.Trim();
   _pdmCompta->tblHITmpPiece->AsString            = strPiece.Trim();
   _pdmCompta->tblHITmpLettrage->AsString         = strLettrage.Trim();

   ( bIsDebiteur ? _pdmCompta->tblHITmpDebit : _pdmCompta->tblHITmpCredit)->AsCurrency = curMontant;
   ASSERT( ( bIsDebiteur ? _pdmCompta->tblHITmpCredit : _pdmCompta->tblHITmpDebit)->IsNull);

   if( pcRAOData)
   {
     #ifdef _DEBUG
       ASSERT( _pdmCompta->tblHITmpRAOData->DataSize >= sizeof( *pcRAOData));
       int n; const char* pc;
       for( n = sizeof( *pcRAOData), pc = (const char*)pcRAOData; n > 0; n--, pc++) ASSERT( isprint( *pc));
     #endif
     _pdmCompta->tblHITmpRAOData->AsString = AnsiString( (const char*)pcRAOData, sizeof( *pcRAOData));
   }

   _pdmCompta->tblHITmp->Post();
 } catch( Exception& e) {
   _pdmCompta->tblHITmp->Cancel();
   throw EUser( TAG( 40), e);
 }
} /* EcritureInsereLigne() */


/*----------------------------------------------------------------------------*\
------------- EcritureValider() ------------------------------------------------
--------------------------------------------------------------------------------
-                                                                              -
-    Cette fonction permet de valider l'écriture, exprimée dans la devise N du -
- dossier, (contenue dans la table temporaire du HI) dans le HI du dossier.    -
-                                                                              -
- Paramètres :                                                                 -
-   nIdEcriture     : ID de l'écriture à valider (IdEcritureNouvelle pour      -
-                     créer une nouvelle écriture, autre valeur pour modifier  -
-                     l'écriture identifiée par cet ID).                       -
-   eHIFlagEcriture : Type de l'écriture ( TTableHI::HIFLAGS_IMPORTEE/NORMALE) -
-   pbarre          : Barre de progression à utiliser (NULL = aucune)          -
\*----------------------------------------------------------------------------*/
void __fastcall EcritureValider( int                    nIdEcriture,
                                 enum TTableHI::HIFlags eHIFlagEcriture,
                                 CBarre*                pbarre)
{
 ASSERT_HI();
 ASSERT( _pdmCompta->tblJnx->Exist( _strEcrJournal));
 ASSERT( ( eHIFlagEcriture == TTableHI::HIFLAGS_NORMALE) ||
         ( eHIFlagEcriture == ( TTableHI::HIFLAGS_NORMALE | TTableHI::HIFLAGS_A_CONTREPASSER)) ||
         ( ( eHIFlagEcriture == TTableHI::HIFLAGS_IMPORTEE) &&
           ( nIdEcriture == IdEcritureNouvelle)));


 ////////////////////////////////////////////////////
 // Validation de l'écriture dans le HI du dossier //
 ////////////////////////////////////////////////////
 _pdmCompta->tblHITmp->ValideEcriture( nIdEcriture,                            // Référence de l'écriture à passer  ( = IdEcritureNul si nouvelle ecriture )
                                       _eRaoIDEcriture,	                       // Id Pour les assistants
                                       eHIFlagEcriture,
                                       *DeviseDefaut,                          // Devise du dossier
                                       _dtImputation,                          // Date de passage de l'écriture
                                       _strEcrJournal.c_str(),                 // Journal où est passée l'écriture
                                       _bPieceConst, _strPieceConst,           // Pièce constante + Nature Pièce (vide)
                                       EmptyStr, NULL,
                                       _pdmCompta->tblHI,
                                       _pdmCompta->tbl2H,
                                       _pdmCompta->tblPC,
                                       EmptyStr,
                                       pbarre);
}


//***********************************************//
// APIs supplémentaires pour la version statique //
//***********************************************//
//#ifndef USEPACKAGES

/*----------------------------------------------------------------------------*\
------------- OemToAnsiString() ------------------------------------------------
--------------------------------------------------------------------------------
-                                                                              -
-    Cette fonction permet de convertir une chaîne C OEM (issue d'un assistant)-
- en Objet AnsiString utilisable sous C++ Builder.                             -
-                                                                              -
- Paramètres :                                                                 -
-   pcsz : Chaîne C OEM à convertir.                                           -
-                                                                              -
- Retour :                                                                     -
-   La chaîne convertie en Ansi pour C++ Builder.                              -
\*----------------------------------------------------------------------------*/
static AnsiString __fastcall OemToAnsiString(const char *pcsz)
{
  char szBuf[ 100]; // Buffer de conversion

  ASSERT(pcsz);
  ASSERT(strlen(pcsz) < sizeof(szBuf));

  ::OemToChar(strcpy(szBuf,pcsz),szBuf);

  ASSERT_VALID_STRING(szBuf);

  return AnsiString(szBuf);
}


/*----------------------------------------------------------------------------*\
------------- AnsiStringToOem() ------------------------------------------------
--------------------------------------------------------------------------------
-                                                                              -
-    Cette fonction permet de convertir un objet AnsiString C++ Builder en une -
- chaîne C OEM (à destination d'un assistant).                                 -
-                                                                              -
- Paramètres :                                                                 -
-   psz     : Chaîne C OEM resultant de la conversion                          -
-   nMaxLen : Nb maximal de caractères à convertir (non compris le '\0' final) -
-   str     : Chaîne C++ Builder à convertir.                                  -
\*----------------------------------------------------------------------------*/
/*static void __fastcall AnsiStringToOem(char *psz,int nMaxLen,const AnsiString &str)
{
  ASSERT(psz);
  ASSERT(nMaxLen >= 0);

  *(strncpy(psz,str.c_str(),nMaxLen) + nMaxLen) = '\0';
  ::CharToOem(psz,psz);
}*/


static
short __fastcall DllComptaAPIFailed(EICTag tag,const Exception &e)
{
  EUser *eTagged;

  eTagged = NULL;
  try {
    eTagged = new EUser(tag,e);
    EICDisplayExceptionMsg(*eTagged,"L'assistant va se terminer");
  } catch(...) {
    EICDisplayException(tag,e);
  }
  delete eTagged;

  return CPTA32_FAIL;
}


 //#############################################################//
 // Fonction d'initialisation externe (via un assistant 16bits) //
 //#############################################################//
 /*----------------------------------------------------------------------------*\
 ------------- OpenAlias() ------------------------------------------------------
 --------------------------------------------------------------------------------
 -                                                                              -
 -     Cette procédure permet de créer, dans la session en cours, une nouvelle  -
 - base de données associée à un alias (qui doit déjà exister dans le BDE).     -
 -                                                                              -
 - Paramètres :                                                                 -
 -   tag             : Tag de l'opération                                       -
 -   pcszAliasName   : Nom de l'alias associé à la base de données à créer et   -
 -                     ouvrir.                                                  -
 -                                                                              -
 - Note : Si une erreur survient pendant le traitement, une exception EUser est -
 -        levée avec le tag fourni en paramètre.                                -
 \*----------------------------------------------------------------------------*/
 static
 void  __fastcall OpenAlias( EICTag      tag,
                             const char* pcszAliasName)
 {
  TDatabase* pdb; // La base de données à créer dans la session

  pdb = NULL;


  try {
    AnsiString strAliasName( pcszAliasName);


    ////////////////////////////////////////////
    // Vérification de l'existance de l'alias //
    ////////////////////////////////////////////
    if( !Session->IsAlias( strAliasName))
      throw EUser( tag, EICFormat(MAKEINTRESOURCE( IDS_EXCEPTION_FmtALIAS_NOT_EXISTS), pcszAliasName));


    ////////////////////////////////////
    // Création de la base de données //
    ////////////////////////////////////
    pdb                 = new TDatabase( Session);
    ASSERT( pdb->KeepConnection);
//    pdb->HandleShared   = true;
    pdb->DatabaseName   = strAliasName;
    pdb->Name           = strAliasName;
    pdb->TransIsolation = tiDirtyRead; // Car utilisation de tables Paradox
    pdb->Open();
  } catch( Exception& e) {
    delete pdb;
    throw EUser( tag, e);
  }
 } /* OpenAlias() */


 static
 void __fastcall ThrowErrNo( EICTag tag)
 {
  DWORD dwErrCode; // Code erreur système correspondant à errno

  switch( errno)
  { case ENOMEM : dwErrCode = ERROR_NOT_ENOUGH_MEMORY;       break;
    case ENOENT : dwErrCode = ERROR_FILE_NOT_FOUND;    break; // Peut aussi indiquer un répertoire non trouvé !!!
    case ENODEV : dwErrCode = ERROR_BAD_UNIT;          break;
    case ERANGE : dwErrCode = ERROR_INVALID_PARAMETER; break;
    default     : throw EUser( tag, EICFormat(MAKEINTRESOURCE( IDS_EXCEPTION_FmtERRNO_ERROR), (unsigned int)errno));
  }
  ::SetLastError( dwErrCode);
  throw ELastError( tag);
 } /* ThrowErrNo() */




 //*************************************//
 // Fonctions sur le numéros de comptes //
 //*************************************//
 /*############################################################################*\
 ############# CptaIsCompte() ###################################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet d'extraire  #
 # du PC le 1er compte supérieur ou égal à un autre compte.                     #
 #                                                                              #
 # Paramètres :                                                                 #
 #   pcszCompteATester : Compte à tester.                                       #
 #   pcszRacine        : Racine - uniquement numérique - à laquelle le compte   #
 #                       doit appartenir (NULL pour aucune racine particulière).#
 #                                                                              #
 # Retour :                                                                     #
 #   CPTA32_YES si le compte est valide; CPTA32_NO sinon (et un message d'erreur#
 # est affiché par la fonction).                                                #
 \*############################################################################*/
 DLLCOMPTAAPI(short) CptaIsCompte( const char* pcszCompteATester,
                                   const char* pcszRacine)
 {
  char c;
  #ifdef _DEBUG
   ASSERT( pcszCompteATester);
   ASSERT(!::IsBadReadPtr( pcszCompteATester, 14));
   ASSERT( strlen( pcszCompteATester) <= 13);
   for( const char* pszCpt = pcszCompteATester; *pszCpt; pszCpt++)
    {
        if (!(isdigit( *pszCpt) || !islower( *pszCpt)))
            ShowMessage(AnsiString("Compte incorrect : ") + pcszCompteATester);
        ASSERT( isdigit( *pszCpt) || !islower( *pszCpt));
    }
   if( pcszRacine) for( const char* pszCpt = pcszRacine; *pszCpt; pszCpt++)
    ASSERT( isdigit( *pszCpt));
  #endif

  try {
    switch( *pcszCompteATester)
    { // Compte vide => Toujours valide
      case '\0' :
        break;

      // Compte de classe "1" à "8" => Toujours valide
      case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : case '8' : case '9' :
        break;

      // Autre classe de compte (notemment alpha) => Toujours invalide
      default :
        try
          {
            if (!TdmCompta::ComptesAvecMauvaiseClasse)
              TdmCompta::ComptesAvecMauvaiseClasse = new TStringList();
            if (TdmCompta::ComptesAvecMauvaiseClasse->IndexOf(pcszCompteATester) == -1)
              {
                TdmCompta::ComptesAvecMauvaiseClasse->Add(pcszCompteATester);
                EICAttention("Classe de compte non reconnue pour le compte '" + AnsiString(pcszCompteATester) + "'.\nToutes les lignes de chaque écriture doivent :\n - soit concerner exclusivement des comptes des classes 1 à 7\n - soit concerner exclusivement des comptes de la classe 8 (compta association) ou 9 (compta analytique)\n\nAbandon de l'opération");
              }
          }
        catch (Exception &e)
          {
            EICDisplayException(TAGL,e);
          }
        return CPTA32_NO;
    }
  for (int i = strlen(pcszCompteATester) - 1;i > 0;i--)          //i > 0 pour ne pas tester le premier caractère qui est déjà testé avant
    {
      c = *(pcszCompteATester + i);
      if (!(((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9'))))
        {
          EICInformation(AnsiString("Compte '") + pcszCompteATester + "' non valide.\n\nVous devez lancer la réparation comptable sur ce dossier\ndans le menu 'Outils/Réparation comptable...' du Menu Fédérateur.");
          return CPTA32_NO;                                      //Il n'y a pas que des chiffres et des lettres
        }
    }

    ////////////////////////////////////////
    // Appartenance du compte à la racine //
    ////////////////////////////////////////
    if( pcszRacine)
      if( strncmp( pcszCompteATester, pcszRacine, strlen( pcszRacine)))
      { // Erreur: Le compte n'appartient pas à la racine
        return CPTA32_NO;
      }
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 70), e);
  }
  return CPTA32_YES;
 } /* CptaIsCompte() */

 DLLCOMPTAAPI(void) CptaGetCompteFormate( char* pcszCompteATester, int nLenBuffer)
 {
    strncpy( pcszCompteATester, CEditCompte::CM_CompleteCompte( pcszCompteATester).c_str(), nLenBuffer);
 }

 DLLCOMPTAAPI(void) CptaGetJournaux( char* pcszJnlODR, int nLenJnlODR,
                                     char* pcszJnlINVR, int nLenJnlINVR,
                                     char* pcszJnlINVRC, int nLenJnlINVRC,
                                     char* pcszJnlCONT, int nLenJnlCONT)
 {
    strncpy( pcszJnlODR,   "ODR",  nLenJnlODR);
    strncpy( pcszJnlINVR,  "INVR", nLenJnlINVR);
    strncpy( pcszJnlINVRC, "INVR", nLenJnlINVRC);
    strncpy( pcszJnlCONT,  "CONT", nLenJnlCONT);
 }

 /*############################################################################*\
 ############# PCOpen() #########################################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet d'ouvrir le #
 # PC du dossier.                                                               #
 #                                                                              #
 # Retour :                                                                     #
 #   CPTA32_YES si le PC a correctement été ouvert; CPTA32_FAIL sinon.          #
 \*############################################################################*/
 DLLCOMPTAAPI(short) PCOpen()
 {
  try {
    OpenTables();
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 80), e);
  }

  ASSERT_PC();

  return CPTA32_YES;
 } /* PCOpen() */


 DLLCOMPTAAPI(short) PCClose()
 {
  ASSERT_PC();

  try {
    CloseTables();
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 90), e);
  }

  return CPTA32_YES;
 } /* PCClose() */


 static
 void __fastcall PCBaseToEmul( CPTA_PC* pPC)
 {
  ASSERT_PC();
  ASSERT( pPC);
  ASSERT( !::IsBadWritePtr( pPC, sizeof( *pPC)));

  memset( pPC, 0, sizeof( *pPC));

  ASSERT( _pdmCompta);
  ASSERT( _pdmCompta->tblPCCompte->AsString.Length() < sizeof( pPC->szCompte));
  ASSERT( _pdmCompta->tblPCIntitule->AsString.Length() < sizeof( pPC->szIntitule));

  strncpy( pPC->szCompte, _pdmCompta->tblPCCompte->AsString.c_str(), sizeof( pPC->szCompte) - 1);
//  /*AnsiStringToOem*/( pPC->szIntitule, sizeof( pPC->szIntitule) - 1, _pdmCompta->tblPCIntitule->AsString);
  strncpy(pPC->szIntitule,_pdmCompta->tblPCIntitule->AsString.c_str(),_pdmCompta->tblPCIntitule->AsString.Length() + 1);

  pPC->dSoldeN = ( pPC->dDebitN  = _pdmCompta->tblPCDebit->AsFloat) -
                 ( pPC->dCreditN = _pdmCompta->tblPCCredit->AsFloat);

  if( ( pPC->dSoldeNm1 = _pdmCompta->tblPCSoldeNm1->AsFloat) >= 0)
    pPC->dDebitNm1  = pPC->dSoldeNm1;
  else
    pPC->dCreditNm1 = -pPC->dSoldeNm1;

  if( ( pPC->dSoldeNm2 = _pdmCompta->tblPCSoldeNm2->AsFloat) >= 0)
    pPC->dDebitNm2  = pPC->dSoldeNm2;
  else
    pPC->dCreditNm2 = -pPC->dSoldeNm2;

  if( ( pPC->dSoldeNm3 = _pdmCompta->tblPCSoldeNm3->AsFloat) >= 0)
    pPC->dDebitNm3  = pPC->dSoldeNm3;
  else
    pPC->dCreditNm3 = -pPC->dSoldeNm3;

  if( ( pPC->dSoldeNm4 = _pdmCompta->tblPCSoldeNm4->AsFloat) >= 0)
    pPC->dDebitNm4  = pPC->dSoldeNm4;
  else
    pPC->dCreditNm4 = -pPC->dSoldeNm4;

  pPC->bIsInactif = !_pdmCompta->tblPC->EstDejaMouvemente;

  pPC->eCodeCycle = (unsigned short)_pdmCompta->tblPCCycle->AsInteger;
 } /* PCBaseToEmul() */


 /*############################################################################*\
 ############# PCGetGTEQ() ######################################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet d'extraire  #
 # du PC le 1er compte supérieur ou égal à un autre compte.                     #
 #                                                                              #
 # Paramètres :                                                                 #
 #   pcszCompte : Compte de référence pour la recherche ("" pour le 1er compte  #
 #                du PC).                                                       #
 #   pPC        : Retourne le compte trouvé (n'est pas modifié en sortie s'il   #
 #                n'y a pas de compte >= à celui précisé).                      #
 #                                                                              #
 # Retour :                                                                     #
 #   CPTA32_YES/CPTA32_NO si un compte >= à pcszCompte a été/n'a pas été trouvé #
 # ou bien CPTA32_FAIL si une erreur survient.                                  #
 \*############################################################################*/
 DLLCOMPTAAPI(short) PCGetGTEQ( const char FAR* pcszCompte,
                                CPTA_PC FAR*    pPC)
 {
  ASSERT( pcszCompte && pPC);
  ASSERT( !::IsBadReadPtr( pPC, 14));
  ASSERT( !::IsBadWritePtr( pPC, sizeof( *pPC)));
  ASSERT_PC();
  ASSERT( !_pdmCompta->tblPC->Filtered);

  try {
    ASSERT( _pdmCompta->tblPC->IndexName.IsEmpty());

    AnsiString strCompte( pcszCompte);

    if( strCompte.IsEmpty())
    { // Compte vide
      // => On se place sur le 1er compte du PC
      _pdmCompta->tblPC->First();
    }
    else
    { // Compte spécifique
      // => On se place sur le plus proche (>=)
      _pdmCompta->tblPC->SetKey();

      _pdmCompta->tblPCCompte->AsString   = strCompte;
      _pdmCompta->tblPCCycle->AsInteger   = 0;
      _pdmCompta->tblPCTypeEnr->AsInteger = TTablePC::PCTYPEENR_COMPTE;

      _pdmCompta->tblPC->GotoNearest();
    }
    if( _pdmCompta->tblPCCompte->AsString >= strCompte)
      do
      {
        if( _pdmCompta->tblPCTypeEnr->AsInteger == TTablePC::PCTYPEENR_COMPTE)
        { // Un compte >= a bien été trouvé
          PCBaseToEmul( pPC);
          return CPTA32_YES;
        }
        _pdmCompta->tblPC->Next();
      } while( !_pdmCompta->tblPC->Eof);
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 100), e);
  }
  return CPTA32_NO;
 } /* PCGetGTEQ() */


 /*############################################################################*\
 ############# PCGetSuivant() ###################################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet d'extraire  #
 # du PC le compte suivant.                                                     #
 #                                                                              #
 # Paramètres :                                                                 #
 #   pPC : Retourne le compte trouvé (n'est pas modifié en sortie s'il n'y a    #
 #         plus de compte suivant dans le PC).                                  #
 #                                                                              #
 # Retour :                                                                     #
 #   CPTA32_YES/CPTA32_NO si le compte suivant a été/n'a pas été trouvé ou bien #
 # CPTA32_FAIL si une erreur survient.                                          #
 \*############################################################################*/
 DLLCOMPTAAPI(short) PCGetSuivant( CPTA_PC FAR* pPC)
 {
  ASSERT( pPC);
  ASSERT_PC();
  ASSERT( !_pdmCompta->tblPC->Filtered);

  try {
    _pdmCompta->tblPC->Next();
    while( !_pdmCompta->tblPC->Eof)
    {
      if( _pdmCompta->tblPCTypeEnr->AsInteger == TTablePC::PCTYPEENR_COMPTE)
      { // Un compte suivant existe bien
        // => Retour du compte
        PCBaseToEmul( pPC);
        return CPTA32_YES;
      }
      _pdmCompta->tblPC->Next();
    }
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 110), e);
  }
  return CPTA32_NO;
 } /* PCGetSuivant() */


  /*############################################################################*\
 ############# PCCreerCompteDlg() ###############################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet de créer un #
 # nouveau compte dans le PC - qui doit être fermé - via le dialogue standard   #
 # de création d'un compte.                                                     #
 #                                                                              #
 # Paramètres :                                                                 #
 #   szCompte   : Retourne le n° du compte créé.                                #
 #   szIntitule : Retourne l'intitulé du compte créé.                           #
 #   cszRacine  : Racine du compte à créer (NULL ou "" pour aucune).            #
 #                                                                              #
 # Retour :                                                                     #
 #   CPTA32_YES si un nouveau compte a bien été créé; CPTA32_NO si l'utilisateur#
 # a abandonné la création du compte ou CPTA32_FAIL si une erreur survient.     #
 \*############################################################################*/
 DLLCOMPTAAPI(short) PCCreerCompteDlg( HWND h,
                                       char FAR       szCompte[ 13 + 1],
                                       char FAR       szIntitule[ 35 + 1],
                                       const char FAR cszRacine[ 13 + 1] /* = NULL */)
 {
  short shRetCode; // Code retour de la fonction
  bool  bOpenned,  // TRUE si les tables de la compta ont été ouvertes
        bSaisieCompte = true;

  #ifdef _DEBUG
  if (_pdmCompta)
    {
      ASSERT(_pdmCompta->tblPC);
//      ASSERT( !_pdmCompta->tblPC->Active); // Le PC doit être fermé
      ASSERT( !_pdmCompta->tblPC->Filtered);
    }
  #endif


  shRetCode = CPTA32_NO;
  bOpenned  = false;
  try {
    if( !szCompte)                               EICThrowInvalidArgument( TAG( 130));
    if( !szIntitule)                             EICThrowInvalidArgument( TAG( 131));
    if( cszRacine && ( strlen( cszRacine) > 13)) EICThrowInvalidArgument( TAG( 132));
    CCompte cptCree;

    if( strlen( szCompte))
    {
        cptCree.Compte = szCompte;
        cptCree.Intitule = szIntitule;
        bSaisieCompte = false;
    }
    if( !_pdmCompta || !_pdmCompta->tblPC->Active)
    {
        OpenTables();
        bOpenned = true;
    }
    if( _pdmCompta->tblPC->CreerCompteDlg( TAG( 133), bSaisieCompte, cptCree))
    { // L'utilisateur a bien créé un nouveau compte
      // => Retour des caractéristiques du compte
      ASSERT( ( cptCree.Compte.Length() <= 13) &&
              ( cptCree.Intitule.Length() <= 35));

      memset(szCompte,0,14);
      strncpy( szCompte, cptCree.Compte.c_str(), 13);
      strncpy( szIntitule, cptCree.Intitule.c_str(), 35);

      // => Notification du succès de l'opération
      shRetCode = CPTA32_YES;
    }
    if( bOpenned) CloseTables();
  } catch( Exception& e) {
    if( bOpenned) CloseTables();
    shRetCode = DllComptaAPIFailed( TAG( 134), e);
  }
  return shRetCode;
 } /* PCCreerCompteDlg() */


 DLLCOMPTAAPI(short) HIOpen()
 {
  try {
    OpenTables();
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 140), e);
  }

  ASSERT_HI();

  return CPTA32_YES;
 } /* HIOpen() */



 DLLCOMPTAAPI(short) HIClose()
 {
  ASSERT_HI();

  try {
    CloseTables();
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 150), e);
  }
  return CPTA32_YES;
 } /* HIClose() */


 static
 void __fastcall HIBaseToEmul( CPTA_HI FAR* pHI)
 {
  int           nRaoIDEcriture;
  unsigned char cRAOData[ 40];  // Données RAO de l'écriture


  ASSERT( pHI );
  ASSERT_HI();


  memset( pHI, 0, sizeof( *pHI));

  ASSERT( _pdmCompta->tblHICompte          ->AsString.Length() < sizeof( pHI->szCompte));
  ASSERT( _pdmCompta->tblHILibelleOperation->AsString.Length() < sizeof( pHI->szLibelle));

  strncpy( pHI->szCompte, _pdmCompta->tblHICompte->AsString.c_str(),  sizeof( pHI->szCompte) - 1);
//  /*AnsiStringToOem*/( pHI->szLibelle, sizeof( pHI->szLibelle) - 1, _pdmCompta->tblHILibelleOperation->AsString);
//  /*AnsiStringToOem*/( pHI->szNoPiece, sizeof( pHI->szNoPiece) - 1, _pdmCompta->tblHIPiece           ->AsString);
  strncpy(pHI->szLibelle,_pdmCompta->tblHILibelleOperation->AsString.c_str(),_pdmCompta->tblHILibelleOperation->AsString.Length() + 1);
  strncpy(pHI->szNoPiece,_pdmCompta->tblHIPiece->AsString.c_str(),_pdmCompta->tblHIPiece->AsString.Length() + 1);

  // MAJ des données RAO
  if( !_pdmCompta->tbl2HRAOData->IsNull)
  {
    ASSERT( _pdmCompta->tbl2HRAOData->DataSize <= sizeof( cRAOData));

    _pdmCompta->tbl2HRAOData->GetData( cRAOData, true);
    memcpy( &pHI->RAOModuleData, cRAOData, sizeof( pHI->RAOModuleData));
  }
  else
  {
    memset( &pHI->RAOModuleData, ' ', sizeof( pHI->RAOModuleData));
  }

  // != 0 si ligne d'écriture lettrée
  if( !_pdmCompta->tblHILettrage->IsNull) { pHI->bIsLettree = true;   }
  else                                    { ASSERT( !pHI->bIsLettree);}

  pHI->dateImputation << _pdmCompta->tblHIDateImputation->AsDateTime;

  // != 0 si le montant de l'opération est débiteur
  if( _pdmCompta->tblHI->EstCrediteur) { ASSERT( !pHI->bIsDebiteur); }
  else                                 { pHI->bIsDebiteur = true;    }

  // Montant de l'opération
  pHI->dMontant = _pdmCompta->tblHIMontant->AsFloat;

  // Clef pour repérer l'enregistrement
  // -> A transmettre à CptaHIUpdateRAOData()/CptaHIGetRAOData() seulement
  pHI->lIDForUpdate = _pdmCompta->tblHIAutoID->AsInteger;

  // ID du module émetteur de l'écriture
  if( ( nRaoIDEcriture = _pdmCompta->tbl2HRAOIDLigne->AsInteger) > 0)
  {
    ASSERT( pHI->szRAOPrefixeModule[ 2] == '\0');
    pHI->szRAOPrefixeModule[ 0] = LOBYTE( HIWORD( nRaoIDEcriture));
    pHI->szRAOPrefixeModule[ 1] = HIBYTE( HIWORD( nRaoIDEcriture));
    pHI->cRAOIDEcriture         = LOBYTE( nRaoIDEcriture);
  }

  // Journal
  AnsiString strJnl( _pdmCompta->tblHICodeJournal->AsString);

//  /*AnsiStringToOem*/( pHI->szCodeJournal, sizeof( pHI->szCodeJournal) - 1, strJnl);
  strncpy(pHI->szCodeJournal,strJnl.c_str(),strJnl.Length() + 1);
  switch( _pdmCompta->tblJnx->Nature( strJnl))
  {
    case JNNATURE_A_NOUVEAUX : pHI->eTypeJournal = CPTA_JNAL_A_NOUVEAU;  break;
    case JNNATURE_ACHATS     : pHI->eTypeJournal = CPTA_JNAL_ACHATS;     break;
    case JNNATURE_VENTES     : pHI->eTypeJournal = CPTA_JNAL_VENTES;     break;
    case JNNATURE_TRESORERIE : pHI->eTypeJournal = CPTA_JNAL_TRESO;      break;
    case JNNATURE_INVENTAIRE : pHI->eTypeJournal = CPTA_JNAL_INVENTAIRE; break;
    case JNNATURE_BALANCE    : pHI->eTypeJournal = CPTA_JNAL_BALANCE;    break;
    default                  : ASSERT( _pdmCompta->tblJnx->Nature( strJnl) == JNNATURE_OD);
                               pHI->eTypeJournal = CPTA_JNAL_OD;         break;
  }
  pHI->IDEcriture = _pdmCompta->tblHIIDEcriture->AsInteger;
  if (!_pdmCompta->tbl2HRANJnlO->IsNull)
    {
      strJnl = _pdmCompta->tbl2HRANJnlO->AsString;
      strncpy(pHI->szCodeJournalOrigine,strJnl.c_str(),strJnl.Length() + 1);
    }
  if (!_pdmCompta->tbl2HRANDateO->IsNull)
    pHI->dateOrigine << _pdmCompta->tbl2HRANDateO->AsDateTime;
 } /* HIBaseToEmul() */


class LettragePeriode
{
public:
  AnsiString Compte;
  AnsiString Lettre;
  int FlagLettrage;
  bool __fastcall operator ==(const LettragePeriode &lettrage) const
    {
      return (lettrage.Compte == Compte) &&
             (lettrage.Lettre == Lettre) &&
             (lettrage.FlagLettrage == FlagLettrage);
    }
  bool __fastcall operator <(const LettragePeriode &lettrage) const
    {
      if (lettrage.Compte != Compte)
        return lettrage.Compte < Compte;
      if (lettrage.FlagLettrage != FlagLettrage)
        return lettrage.FlagLettrage < FlagLettrage;
      return lettrage.Lettre < Lettre;
    }
};
typedef std::map<LettragePeriode,TDateTime> MapLettragePeriode;
typedef MapLettragePeriode::iterator Maplpi;

MapLettragePeriode Maplp;

DLLCOMPTAAPI(short) InitLettragePeriode(const date FinDePeriode)
{
  TDateTime DateFinDePeriode;
  LettragePeriode lp;
  Maplpi mlpi,mlpi2;

  ASSERT_HI();

  try
    {
      DateFinDePeriode << FinDePeriode;
      Maplp.clear();
      SCAN_TABLE(_pdmCompta->tblHI)           //On fait la liste de tous les lettrages de l'histo
        if (LIGNE_EST_LETTRE(_pdmCompta->tblHIFlagLettrage->AsInteger) || LIGNE_EST_LETTRE_NP1(_pdmCompta->tblHIFlagLettrage->AsInteger))
          {
            lp.Compte = _pdmCompta->tblHICompte->AsString;
            lp.Lettre = _pdmCompta->tblHILettrage->AsString;
            lp.FlagLettrage = _pdmCompta->tblHIFlagLettrage->AsInteger;
            mlpi = Maplp.find(lp);
            if (mlpi == Maplp.end())
              {//Ce lettrage n'existe pas encore dans la liste
                Maplp.insert(MapLettragePeriode::value_type(lp,_pdmCompta->tblHIDateImputation->AsDateTime));
              }
            else
              {//Ce lettrage existe déjà dans la liste
                if (_pdmCompta->tblHIDateImputation->AsDateTime > mlpi->second)
                  mlpi->second = _pdmCompta->tblHIDateImputation->AsDateTime;   //La date d'imputation est supérieure
              }
          }
      for (mlpi = Maplp.begin();mlpi != Maplp.end();)
        {
          mlpi2 = mlpi->second > DateFinDePeriode ? mlpi : Maplp.end();
          mlpi++;
          if (mlpi2 != Maplp.end())
            Maplp.erase(mlpi2);              //On supprime les lettrages étant supérieurs à la date donnée
        }
      return CPTA32_YES;
    }
  catch (Exception &e)
    {
      Maplp.clear();
    }
  return CPTA32_NO;
}






DLLCOMPTAAPI(short) HILigneCouranteEstLettreeTotalementAvantFinDePeriode(const AnsiString &Compte,const AnsiString &Lettrage,int FlagLettrage)
{
  LettragePeriode lp;
  Maplpi mlpi;

  ASSERT_HI();

  try
    {
      lp.Compte = Compte;
      lp.Lettre = Lettrage;
      lp.FlagLettrage = FlagLettrage;
      mlpi = Maplp.find(lp);
      return mlpi == Maplp.end() ? CPTA32_NO : CPTA32_YES;
    }
  catch (Exception &e)
    {
    }
  return CPTA32_NO;
}



DLLCOMPTAAPI(short) DesinitLettragePeriode()
{
  Maplp.clear();
  return CPTA32_YES;
}



 /*############################################################################*\
 ############# HIGetFirstOfRacine() #############################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet d'extraire  #
 # de l'historique la 1ère ligne détail du 1er compte d'une racine donnée.      #
 #                                                                              #
 # Paramètres :                                                                 #
 #   pcszRacine : Racine du 1er compte que l'on veut obtenir ("" pour tous les  #
 #                comptes de l'historique).                                     #
 #   pHI        : Retourne le compte trouvé (n'est pas modifié en sortie si     #
 #                aucun compte de la racine n'est trouvé).                      #
 #                                                                              #
 # Retour :                                                                     #
 #   CPTA32_YES/CPTA32_NO si la 1ère ligne détail du 1er compte de la racine    #
 # est/n'est pas trouvé ou bien CPTA32_FAIL si une erreur survient.             #
 #                                                                              #
 # Note : Les lignes détail annulées des compte ne sont pas prises en compte    #
 #        par cette fonction.                                                   #
 \*############################################################################*/
DLLCOMPTAAPI(short) HIGetFirstOfRacine(const char FAR *pcszRacine,CPTA_HI FAR *pHI)
{
  ASSERT(pcszRacine);
  ASSERT(pHI);
  ASSERT_HI();
  ASSERT(!::IsBadWritePtr(pHI,sizeof(*pHI)));

  try {
    _pdmCompta->tblHI->IndexName = TTableHI::striHI_Comptes;

    // => Positionnement sur le 1er compte de la racine
    if( *pcszRacine)
    { // Racine spécifique
      _strEmulRacine = AnsiString( pcszRacine);

      // => Recherche du compte le plus proche (>=) de la racine
      _pdmCompta->tblHI->SetKey();
      _pdmCompta->tblHICompte->AsString = _strEmulRacine;
      _pdmCompta->tblHI->GotoNearest();
    }
    else
    { // Racine vide
      _strEmulRacine = EmptyStr;

      // => On se place sur le 1er compte de l'historique
      _pdmCompta->tblHI->First();
    }
    // Vérification de l'appartenance du compte trouvé à la racine
    if( !_pdmCompta->tblHI->Eof)
      if( ( _pdmCompta->tblHICompte->AsString.Pos( _strEmulRacine) == 1) ||
          _strEmulRacine.IsEmpty())
      { // La ligne détail trouvée appartient bien à un compte de la racine
        if( _pdmCompta->tblHI->EstUneLigneAnnulee)
        { // .. mais cette ligne détail est annulée
          // => Retour de la ligne détail suivante de la racine
          return HIGetNextOfRacine( pHI);
        }
        else
        { // .. et cette ligne détail est valide
          // => Fin normale
          HIBaseToEmul( pHI);
          return CPTA32_YES;
        }
      }
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 160), e);
  }
  return CPTA32_NO;
 } /* HIGetFirstOfRacine() */


 /*############################################################################*\
 ############# HIGetNextOfRacine() ##############################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet d'extraire  #
 # de l'historique la ligne détail suivante d'un compte de la racine précisée   #
 # lors du dernier appel à HIGetFirstOfRacine().                                #
 #                                                                              #
 # Paramètres :                                                                 #
 #   pHI : Retourne la ligne détail trouvée pour un compte de la racine (n'est  #
 #         pas modifié en sortie s'il n'y à plus de compte dans cette racine).  #
 #                                                                              #
 # Retour :                                                                     #
 #   CPTA32_YES/CPTA32_NO si une ligne détail d'un compte de la racine          #
 # est/n'est pas trouvé ou bien CPTA32_FAIL si une erreur survient.             #
 #                                                                              #
 # Note : Les lignes détail annulées des compte ne sont pas prises en compte    #
 #        par cette fonction.                                                   #
 \*############################################################################*/
 DLLCOMPTAAPI(short) HIGetNextOfRacine( CPTA_HI FAR* pHI)
 {
  ASSERT( pHI);
  ASSERT( !::IsBadWritePtr( pHI, sizeof( *pHI)));
  ASSERT_HI();
  ASSERT( !_pdmCompta->tblHI->Filtered);
  ASSERT( _pdmCompta->tblHI->IndexName == TTableHI::striHI_Comptes);
  ASSERT( ( _pdmCompta->tblHICompte->AsString.Pos( _strEmulRacine) == 1) || // On doit déjà être sur un compte de la racine
          _strEmulRacine.IsEmpty());

  try {
    for(;;)
    {
      _pdmCompta->tblHI->Next();
      if( _pdmCompta->tblHI->Eof)
      { // Fin de l'historique atteinte
        break;
      }
      // Vérification de l'appartenance du compte trouvé à la racine
      if( ( _pdmCompta->tblHICompte->AsString.Pos( _strEmulRacine) == 1) ||
          _strEmulRacine.IsEmpty())
      { // La ligne détail actuelle appartient bien à un compte de la racine
        if( _pdmCompta->tblHI->EstUneLigneAnnulee)
        { // .. mais cette ligne détail est annulée
          // => Lecture de la ligne détail suivante
          continue;
        }
        else
        { // .. et cette ligne détail est valide
          // => Fin normale
          HIBaseToEmul( pHI);
          return CPTA32_YES;
        }
      }
      else
      { // Cette ligne détail ne fait plus partie des comptes de la racine
        // => Fin de l'énumération
        break;
      }
    }
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 170), e);
  }
  return CPTA32_NO;
 } /* HIGetNextOfRacine() */


 /*############################################################################*\
 ############# HIGetFirstOfIDEcriture() #########################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet d'extraire  #
 # de l'historique la 1ère ligne détail du 1er compte d'une racine donnée.      #
 #                                                                              #
 # Paramètres :                                                                 #
 #   IDEcriture : Racine du 1er compte que l'on veut obtenir ("" pour tous les  #
 #                comptes de l'historique).                                     #
 #   pHI        : Retourne le compte trouvé (n'est pas modifié en sortie si     #
 #                aucun compte de la racine n'est trouvé).                      #
 #                                                                              #
 # Retour :                                                                     #
 #   CPTA32_YES/CPTA32_NO si la 1ère ligne détail du 1er compte de la racine    #
 # est/n'est pas trouvé ou bien CPTA32_FAIL si une erreur survient.             #
 #                                                                              #
 # Note : Les lignes détail annulées des compte ne sont pas prises en compte    #
 #        par cette fonction.                                                   #
 \*############################################################################*/
DLLCOMPTAAPI(short) HIGetFirstOfIDEcriture(unsigned long IDEcriture,CPTA_HI FAR *pHI)
{
  ASSERT(IDEcriture);
  ASSERT(pHI);
  ASSERT_HI();
  ASSERT(!::IsBadWritePtr(pHI,sizeof(*pHI)));

  try {
    _pdmCompta->tblHI->IndexName = TTableHI::striHI_IDEcritures;

    // => Recherche du compte le plus proche (>=) de la racine
    _pdmCompta->tblHI->SetKey();
    _pdmCompta->tblHIIDEcriture->AsString = _IDEcriture = IDEcriture;
    if (_pdmCompta->tblHI->GotoKey())
      { // La ligne détail trouvée appartient bien à un compte de la racine
        if( _pdmCompta->tblHI->EstUneLigneAnnulee)
        { // .. mais cette ligne détail est annulée
          // => Retour de la ligne détail suivante de la racine
          return HIGetNextOfIDEcriture( pHI);
        }
        else
        { // .. et cette ligne détail est valide
          // => Fin normale
          HIBaseToEmul( pHI);
          return CPTA32_YES;
        }
      }
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 160), e);
  }
  return CPTA32_NO;
 } /* HIGetFirstOfIDEcriture() */


 /*############################################################################*\
 ############# HIGetNextOfIDEcriture() ##########################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet d'extraire  #
 # de l'historique la ligne détail suivante d'un compte de l'IDEcriture précisé #
 # lors du dernier appel à HIGetFirstOfIDEcriture().                            #
 #                                                                              #
 # Paramètres :                                                                 #
 #   pHI : Retourne la ligne détail trouvée pour un compte du même IDEcriture   #
 #         (n'est pas modifié en sortie s'il n'y à plus de ligne dans           #
 #         l'écriture.                                                          #
 #                                                                              #
 # Retour :                                                                     #
 #   CPTA32_YES/CPTA32_NO si une ligne détail de la même écriture               #
 # est/n'est pas trouvé ou bien CPTA32_FAIL si une erreur survient.             #
 #                                                                              #
 # Note : Les lignes détail annulées des compte ne sont pas prises en compte    #
 #        par cette fonction.                                                   #
 \*############################################################################*/
 DLLCOMPTAAPI(short) HIGetNextOfIDEcriture( CPTA_HI FAR* pHI)
 {
  ASSERT(pHI);
  ASSERT( !::IsBadWritePtr(pHI,sizeof(* pHI)));
  ASSERT_HI();
  ASSERT(!_pdmCompta->tblHI->Filtered);
  ASSERT(_pdmCompta->tblHI->IndexName == TTableHI::striHI_IDEcritures);
  ASSERT(_pdmCompta->tblHIIDEcriture->AsInteger == _IDEcriture); // On doit déjà être sur un compte de la racine

  try {
    while (true)
    {
      _pdmCompta->tblHI->Next();
      if( _pdmCompta->tblHI->Eof || (_pdmCompta->tblHIIDEcriture->AsInteger != _IDEcriture))
      { // Fin de l'historique atteinte ou cette ligne détail ne fait plus partie des comptes de la racine
        return CPTA32_NO;
      }
      // Vérification de l'appartenance du compte trouvé à la racine
      // La ligne détail actuelle appartient bien à un compte de la racine
      if (!_pdmCompta->tblHI->EstUneLigneAnnulee)
      { // .. et cette ligne détail est valide
        // => Fin normale
        HIBaseToEmul( pHI);
        return CPTA32_YES;
      }
    }
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 170), e);
  }
  return CPTA32_NO;
 } /* HIGetNextOfIDEcriture() */


 /*############################################################################*\
 ############# HIGetRaoData() ###################################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - retourne les       #
 # données RAO d'une ligne détail donnée.                                       #
 #                                                                              #
 # Paramètres :                                                                 #
 #   lIDRAOData : ID des données RAO à retourner                                #
 #   pRAOData   : Retourne les données RAO demandées.                           #
 #                                                                              #
 # Retour :                                                                     #
 #   CPTA32_YES si tout est Ok; CPTA32_FAIL si une erreur survient.             #
 \*############################################################################*/
 DLLCOMPTAAPI(short) HIGetRaoData( long                lIDRAOData,
                                   CPTA_HIRAODATA FAR* pRAOData)
 {
  ASSERT_HI();
  ASSERT( _pdmCompta->tbl2HRAOData->DataSize >= sizeof( *pRAOData));


  try {
    if((_pdmCompta->tblHIAutoID->AsInteger != (int)lIDRAOData) && !_pdmCompta->tblHI->Locate( TTableHI::strfHI_AutoID, (int)lIDRAOData, TLocateOptions()))
      EICThrowInvalidArgument( TAG( 180));
    if( !_pdmCompta->tbl2HRAOData->IsNull)
    { // Transfert des données RAO
      AnsiString strRAOData( _pdmCompta->tbl2HRAOData->AsString + AnsiString( "            "));

      ASSERT( strRAOData.Length() > sizeof( *pRAOData));
      strncpy( (char*)pRAOData, strRAOData.c_str(), sizeof( *pRAOData));
    }
    else
    { // Pas de données RAO pour cet ID
      memset( (void*)pRAOData, ' ', sizeof( *pRAOData));
    }
  } catch( Exception& e) {
    memset( (void*)pRAOData, ' ', sizeof( *pRAOData));
    return DllComptaAPIFailed( TAG( 181), e);
  }
  #ifdef _DEBUG
    int n; const char* pc;
    for( n = sizeof( *pRAOData), pc = (const char*)pRAOData; n > 0; n--, pc++)
      if( !isprint( *pc))
      {
        EICErreur( EICFormat( "HIGetRaoData(32 bits)\n\nData invalide (ID=%lu): '%s' (len = %i/%i)", lIDRAOData, (const char*)pRAOData, strlen((const char*)pRAOData), sizeof( *pRAOData)).c_str());
        ASSERT( false);
      }
  #endif
  return CPTA32_YES;
 } /* HIGetRaoData() */


 /*############################################################################*\
 ############# HISetRaoData() ###################################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet de mettre   #
 # à jour/initialiser les données RAO d'une ligne détail donnée.                #
 #                                                                              #
 # Paramètres :                                                                 #
 #   lIDRAOData : ID des données RAO à MAJ/Initialiser.                         #
 #   pcRAOData  : Nouvelles données RAO de la ligne détail.                     #
 #                                                                              #
 # Retour :                                                                     #
 #   CPTA32_YES si tout est Ok; CPTA32_FAIL si une erreur survient.             #
 \*############################################################################*/
 DLLCOMPTAAPI(short) HISetRaoData( long                      lIDRAOData,
                                   const CPTA_HIRAODATA FAR* pcRAOData)
 {
  #ifdef _DEBUG
    ASSERT_HI();
    ASSERT( _pdmCompta->tbl2HRAOData->DataSize >= sizeof( *pcRAOData));
    int n; const char* pc;
    for( n = sizeof( *pcRAOData), pc = (const char*)pcRAOData; n > 0; n--, pc++)
      if( !isprint( *pc))
      {
        EICErreur( EICFormat( "HISetRaoData(32 bits)\n\nData invalide (ID=%lu): '%s' (len = %i/%i)", lIDRAOData, (const char*)pcRAOData, strlen((const char*)pcRAOData), sizeof( *pcRAOData)).c_str());
        ASSERT( false);
      }
  #endif

  try {
    if( !_pdmCompta->tblHI->Locate( TTableHI::strfHI_AutoID, (int)lIDRAOData, TLocateOptions()))
      EICThrowInvalidArgument( TAG( 190));
    if (!_pdmCompta->tbl2H->IsEmpty())
	    _pdmCompta->tbl2H->Edit();
    else
	    {
	    	_pdmCompta->tbl2H->Append();
	    	_pdmCompta->tbl2HAutoID->AsInteger = _pdmCompta->tblHIAutoID->AsInteger;
	    }
    _pdmCompta->tbl2HRAOData->AsString = AnsiString( (const char*)pcRAOData, sizeof( *pcRAOData));
    _pdmCompta->tbl2H->Post();
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 191), e);
  }
  return CPTA32_YES;
 } /* HISetRaoData() */


 /*############################################################################*\
 ############# HIGetNbEnreg() ###################################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - retourne le        #
 # nombre total de ligne (ligne annulées comprises) de l'historique.            #
 # lors du dernier appel à HIGetFirstOfRacine().                                #
 #                                                                              #
 # Retour :                                                                     #
 #   Le nombre de comptes du HI (100000 si une erreur survient).                #
 \*############################################################################*/
 DLLCOMPTAAPI(long) HIGetNbEnreg()
 {
  int nCount;

  ASSERT_HI();
  ASSERT( !_pdmCompta->tblHI->Filtered);

  // On retourne le Nb max d'enreg de la table pour ne pas planter la barre d'avancement
  try {
    Dbtables::Check( Bde::DbiGetRecordCount( _pdmCompta->tblHI->Handle, nCount));
  } catch( Exception& e) {
    DllComptaAPIFailed( TAG( 200), e);
    nCount = 100000L;
  }
  return nCount;
 } /* HIGetNbEnreg() */


 /*############################################################################*\
 ############# AssJnCree() ######################################################
 ################################################################################
 #                                                                              #
 #    Cette fonction permet de créer un journal sans passer par la fenêtre de   #
 # paramétrage de REV'AO ou TEN'CO.                                             #
 #                                                                              #
 # Paramètres :                                                                 #
 #   jnl       : Structure du journal à créer.                                  #
 #                                                                              #
 # Valeur de retour :                                                           #
 #   ValRetour : Booléen indiquant si le journal a été créé                     #
 \*############################################################################*/
 bool __fastcall AssJnCree(const CJournal& jnl)
 {
  bool bEstOuvert = false, bJnEstCree = false;
  try
  {
    OpenTables();
    bEstOuvert = true;
    bJnEstCree = _pdmCompta->tblJnx->Creer(jnl);
    CloseTables();
  }
  catch( Exception& e)
  {
    if(bEstOuvert)
      CloseTables();
    EICDisplayException(TAG(205),e);
    bJnEstCree = false;
  }
  return bJnEstCree;
 }

 /*############################################################################*\
 ############# AssGetIDEcriture() ###############################################
 ################################################################################
 #                                                                              #
 #    Cette fonction permet de récupérer l'IDEcriture d'une écriture donnée.    #
 #                                                                              #
 # Paramètres :                                                                 #
 #   CodeJournal      : Nom du journal dans lequel l'écriture est passée.       #
 #   DateImputation   : Date d'imputation de l'écriture.                        #
 #   RaoIDEcriture    : ID ( HIGH( PREFIXE_MODULE), LOW( cIDPerso)) de l'écr.   #
 #   ComplementPiece  : Complement de la pièce de l'écriture                    #
 \*############################################################################*/
 int __fastcall AssGetIDEcriture(const AnsiString &CodeJournal,
                                 const TDateTime &DateImputation,
                                 int RaoIDEcriture,
                                 const AnsiString &ComplementPiece)
{
  char       szPiece[4]; // N° de pièce de l'écriture
  AnsiString Piece;
  bool       bEstOuvert = false;
  int        nIDEcriture = 0;
  try
  {
    OpenTables();
    bEstOuvert = true;


    szPiece[0] = LOBYTE( HIWORD( RaoIDEcriture));
    szPiece[1] = HIBYTE( HIWORD( RaoIDEcriture));
    szPiece[2] = LOBYTE( RaoIDEcriture);
    szPiece[3] = '\0';
    Piece = szPiece + ComplementPiece;
    // Rechecher de IDEcriture
    _pdmCompta->qHI->SQL->Clear();
    _pdmCompta->qHI->SQL->Add("SELECT IDEcriture,CodeJournal FROM REVAO$$H H LEFT OUTER JOIN REVAO$2H H2 ON (H.AutoID=H2.AutoID)");
    _pdmCompta->qHI->SQL->Add(AnsiString(" WHERE H.DateImputation='")+DateImputation.FormatString("mm/dd/yyyy")+"'"
                                           " AND RAOIDLigne="        +RaoIDEcriture+
                                           " AND CodeJournal="       +QuotedStr(CodeJournal)+
                                           " AND Piece="             +QuotedStr(Piece));
    _pdmCompta->qHI->Open();
    if(!_pdmCompta->qHI->IsEmpty())
      nIDEcriture = _pdmCompta->qHIIDEcriture->AsInteger;
    _pdmCompta->qHI->Close();
    _pdmCompta->qHI->SQL->Clear();

    CloseTables();
  }
  catch( Exception& e)
  {
    if(bEstOuvert)
      CloseTables();
    EICDisplayException(TAG(206),e);
    return false;
  }
  return nIDEcriture;
}


 /*############################################################################*\
 ############# EmulEcrOpen() ####################################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet d'ouvrir la #
 # table temporaire du HI pour y créer une écriture qui devra être ensuite      #
 # validée (par EmulEcrValide) pour être insérée dans le HI du dossier.         #
 #                                                                              #
 # Paramètres :                                                                 #
 #   pcszJournal      : Nom du journal dans lequel doit être passée l'écriture. #
 #   pcDateImputation : Date d'imputation de l'écriture.                        #
 #   bContrepasser    : != 0 si l'écriture doit être contrepassée.              #
 #   eRaoIDEcriture   : ID ( HIGH( PREFIXE_MODULE), LOW( cIDPerso)) de l'écr.   #
 \*############################################################################*/
  /*###############################################################*\
 # Cette fonction ne doit être appelée que par les assistants C.   #
 # Les assistants C++ doivents appeler la fonction AssEcrOpen.     #
 \*###############################################################*/
DLLCOMPTAAPI(short) EmulEcrOpen( const char FAR* pcszJournal,
                                  const date FAR* pcdateImputation,
                                  BOOL32          bContrepasser,
                                  INT32           eRaoIDEcriture)
 {
  try {
    TDateTime dtImputation;
    try
      {
        dtImputation = TDateTime(pcdateImputation->a, pcdateImputation->m, pcdateImputation->j);
      }
    catch (Exception &e)
      {
        throw EUser(TAGL,e.Message + "\nParamètres passés : " + pcdateImputation->j + "/" + pcdateImputation->m + "/" + pcdateImputation->a);
      }
    if (AssEcrOpen(pcszJournal,dtImputation,bContrepasser,eRaoIDEcriture))
      return CPTA32_YES;
    return CPTA32_NO;
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 210), e);
  }
 } /* EmulEcrOpen() */

bool __fastcall AssEcrOpen(const AnsiString &CodeJournal,
                           const TDateTime &DateImputation,
                           bool Contrepasser,
                           int RaoIDEcriture)
{
  bool bOpenned;
  int  nIDEcriture; // ID de l'écriture qu'on va ouvrir
  char szPiece[ 4]; // N° de pièce de l'écriture

  bOpenned = false;
  try {
    OpenTables();
    bOpenned = true;

    //ASSERT( !_pdmCompta->tblHI->Filtered);

/*    Variant   vRAOIDDate( VarArrayCreate( OPENARRAY( int, (0, 1)), varVariant));
    vRAOIDDate.PutElement( RaoIDEcriture,  0);
    vRAOIDDate.PutElement( DateImputation, 1);*/
    _pdmCompta->qHI->SQL->Clear();
    _pdmCompta->qHI->SQL->Add("SELECT IDEcriture,CodeJournal FROM REVAO$$H H LEFT OUTER JOIN REVAO$2H H2 ON (H.AutoID=H2.AutoID)");
    _pdmCompta->qHI->SQL->Add("WHERE (H.DateImputation='" + DateImputation.FormatString("mm/dd/yyyy") + "') AND (H2.RAOIDLigne=" + RaoIDEcriture + ")");
    _pdmCompta->qHI->Open();

    if(_pdmCompta->qHI->IsEmpty())
      {
      // => On va créer une nouvelle écriture
      nIDEcriture = IdEcritureNouvelle;
      }
    else
      {
      // => On va Mettre A Jour cette écriture avec la nouvelle
      ASSERT( _pdmCompta->qHICodeJournal->AsString == CodeJournal);
      nIDEcriture = _pdmCompta->qHIIDEcriture->AsInteger;
      }
    _pdmCompta->qHI->Close();
    _pdmCompta->qHI->SQL->Clear();

/*   
    if( _pdmCompta->tblHI->Locate( szfHI_RAOIDLigne ";" szfHI_DateImputation, vRAOIDDate, TLocateOptions()))
    { // Ce module a déjà envoyé une écriture à cette date avec cet ID
      // => On va Mettre A Jour cette écriture avec la nouvelle
      ASSERT( _pdmCompta->tblHICodeJournal->AsString == CodeJournal);
      nIDEcriture = _pdmCompta->tblHIIDEcriture->AsInteger;
    }
    else
    { // Ce module envoie cette écriture pour la 1ère fois
      // => On va créer une nouvelle écriture
      nIDEcriture = IdEcritureNouvelle;
    }  */

    szPiece[ 0] = LOBYTE( HIWORD( RaoIDEcriture));
    szPiece[ 1] = HIBYTE( HIWORD( RaoIDEcriture));
    szPiece[ 2] = LOBYTE( RaoIDEcriture);
    szPiece[ 3] = '\0';
    EcritureOuvrir( CodeJournal,
                    DateImputation,
                    nIDEcriture,
                    true, szPiece, // Pièce constante sur une écriture émulée
                    Contrepasser,
                    RaoIDEcriture);
  } catch( Exception& e) {
    if( bOpenned) CloseTables();
    EICDisplayException(TAG(211),e);
    return false;
  }
  return true;
}


bool __fastcall AssEcrOpen(const AnsiString &CodeJournal,
                           const TDateTime &DateImputation,
                           bool Contrepasser,
                           int RaoIDEcriture,
                           const AnsiString &ComplementPiece)
{
  bool bOpenned;
  int  nIDEcriture; // ID de l'écriture qu'on va ouvrir
  char szPiece[ 4]; // N° de pièce de l'écriture
  AnsiString Piece;

  bOpenned = false;
  try {
    OpenTables();
    bOpenned = true;

    ASSERT( !_pdmCompta->tblHI->Filtered);

    szPiece[ 0] = LOBYTE( HIWORD( RaoIDEcriture));
    szPiece[ 1] = HIBYTE( HIWORD( RaoIDEcriture));
    szPiece[ 2] = LOBYTE( RaoIDEcriture);
    szPiece[ 3] = '\0';
    Piece = szPiece + ComplementPiece;
/*    Variant   vRAOIDDate( VarArrayCreate( OPENARRAY( int, (0, 2)), varVariant));
    vRAOIDDate.PutElement( RaoIDEcriture,  0);
    vRAOIDDate.PutElement( DateImputation, 1);
    vRAOIDDate.PutElement( Piece, 2);*/

    _pdmCompta->qHI->SQL->Clear();
    _pdmCompta->qHI->SQL->Add("SELECT IDEcriture,CodeJournal FROM REVAO$$H H LEFT OUTER JOIN REVAO$2H H2 ON (H.AutoID=H2.AutoID)");
    _pdmCompta->qHI->SQL->Add("WHERE (H.DateImputation='" + DateImputation.FormatString("mm/dd/yyyy") + "') AND (H2.RAOIDLigne=" + RaoIDEcriture + ") AND (H.Piece=" + QuotedStr(Piece) + ")");
    _pdmCompta->qHI->Open();

    if(_pdmCompta->qHI->IsEmpty())
      {
      // => On va créer une nouvelle écriture
      nIDEcriture = IdEcritureNouvelle;
      }
    else
      {
      // => On va Mettre A Jour cette écriture avec la nouvelle
      ASSERT( _pdmCompta->qHICodeJournal->AsString == CodeJournal);
      nIDEcriture = _pdmCompta->qHIIDEcriture->AsInteger;
      }
    _pdmCompta->qHI->Close();
    _pdmCompta->qHI->SQL->Clear();
        
    /*
    if( _pdmCompta->tblHI->Locate( szfHI_RAOIDLigne ";" szfHI_DateImputation ";" szfHI_Piece, vRAOIDDate, TLocateOptions()))
    { // Ce module a déjà envoyé une écriture à cette date avec cet ID
      // => On va Mettre A Jour cette écriture avec la nouvelle
      ASSERT( _pdmCompta->tblHICodeJournal->AsString == CodeJournal);
      nIDEcriture = _pdmCompta->tblHIIDEcriture->AsInteger;
    }
    else
    { // Ce module envoie cette écriture pour la 1ère fois
      // => On va créer une nouvelle écriture
      nIDEcriture = IdEcritureNouvelle;
    }   */

    EcritureOuvrir( CodeJournal,
                    DateImputation,
                    nIDEcriture,
                    true, Piece, // Pièce constante sur une écriture émulée
                    Contrepasser,
                    RaoIDEcriture);
  } catch( Exception& e) {
    if( bOpenned) CloseTables();
    EICDisplayException(TAG(211),e);
    return false;
  }
  return true;
}


 /*############################################################################*\
 ############# EmulEcrInsLigne() ################################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet d'insérer   #
 # dans la table temporaire du HI une ligne d'une écriture qui devra être       #
 # ensuite validée (par EmulEcrValide) pour être insérée dans le HI du dossier. #
 #                                                                              #
 # Paramètres :                                                                 #
 #   pcszCompte           : Compte à imputer                                    #
 #   pcszLibelleOperation : Libellé de l'écriture                               #
 #   pcdMontant           : Montant de l'écriture                               #
 #   bIsDebiteur          : TRUE si le montant est débiteur                     #
 #   pcRAOData            : Donnée de révision pour cette ligne d'écriture (NULL#
 #                          pour aucune donnée)                                 #
 \*############################################################################*/
 /*###############################################################*\
 # Cette fonction ne doit être appelée que par les assistants C.   #
 # Les assistants C++ doivents appeler la fonction AssEcrInsLigne. #
 \*###############################################################*/
 DLLCOMPTAAPI(short) EmulEcrInsLigne( const char FAR*           pcszCompte,
                                      const char FAR*           pcszLibelleOperation,
                                      const double FAR*         pcdMontant,
                                      BOOL32                    bIsDebiteur,
                                      const CPTA_HIRAODATA FAR* pcRAOData)
 {
  EICTag tag;

  ASSERT_HI();

  try {
    if(      !pcszCompte || !*pcszCompte)                  tag = TAG( 220);
//    else if( !pcszLibelleOperation)                        tag = TAG( 221);
    else if( !pcdMontant || ( *pcdMontant == (double)0.0)) tag = TAG( 222);
    else tag = 0; // Paramètres Ok
    if (tag)
      EICThrowInvalidArgument( tag);
    if (AssEcrInsLigne(pcszCompte,OemToAnsiString(pcszLibelleOperation),*pcdMontant,bIsDebiteur,pcRAOData))
      return CPTA32_YES;
    return CPTA32_NO;
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 223), e);
  }
 } /* EmulEcrInsLigne() */

bool __fastcall AssEcrInsLigne(const AnsiString &Compte,
                               const AnsiString &LibelleOperation,
                               const Currency &Montant,
                               bool IsDebiteur,
                               const CPTA_HIRAODATA FAR* pcRAOData)
{
  EICTag tag;

  ASSERT_HI();

  try {
    if (Compte.IsEmpty()) tag = TAG( 220);
//    else if (LibelleOperation.IsEmpty()) tag = TAG( 221);
    else if (Montant == 0.0) tag = TAG( 222);
    else tag = 0; // Paramètres Ok
    if (tag)
      EICThrowInvalidArgument( tag);
    EcritureInsereLigne( Compte,
                         LibelleOperation,
                         Montant,
                         IsDebiteur,
                         _strPieceConst, // Pièce
                         EmptyStr,       // Lettrage
                         pcRAOData);
  } catch( Exception& e) {
    EICDisplayException(tag,e);
    _pdmCompta->tblHITmp->Cancel();
    CloseTables();
    return false;
  }
  return true;
}


 /*############################################################################*\
 ############# EmulEcrAnnule() ##################################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet d'annuler   #
 # une écriture passée par un module.                                           #
 #                                                                              #
 # Paramètres :                                                                 #
 #   pcszJournal      : Nom du journal dans lequel doit être l'écriture à annul.#
 #   pcDateImputation : Date d'imputation de l'écriture à annuler.              #
 #   nRaoIDEcriture   : ID ( HIGH( PREFIXE_MODULE), LOW( cIDPerso)) de l'écr.   #
 \*############################################################################*/
   /*###############################################################*\
 # Cette fonction ne doit être appelée que par les assistants C.   #
 # Les assistants C++ doivents appeler la fonction AssEcrAnnule.   #
 \*###############################################################*/
DLLCOMPTAAPI(short) EmulEcrAnnule( const char FAR* pcszJournal,
                                    const date FAR* pcdtImputation,
                                    int             nRaoIDEcriture)
 {
  try {
    if (AssEcrAnnule(pcszJournal,TDateTime( (unsigned short)pcdtImputation->a, (unsigned short)pcdtImputation->m, (unsigned short)pcdtImputation->j),nRaoIDEcriture))
      return CPTA32_YES;
    return CPTA32_NO;
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 230), e);
  }
 } /* EmulEcrAnnule() */

bool __fastcall AssEcrAnnule(const AnsiString &CodeJournal,
                             const TDateTime &DateImputation,
                             int RaoIDEcriture)
{
  EICTag tag;
  bool   bOpenned = false;
  short  sReturn = CPTA32_YES;

  try {
    tag = TAG( 231); OpenTables();
    bOpenned = true;

/*    Variant vRAOIDCodeDate( VarArrayCreate( OPENARRAY( int, (0, 2)), varVariant));

    vRAOIDCodeDate.PutElement( RaoIDEcriture,  0);
    vRAOIDCodeDate.PutElement( CodeJournal,    1);
    vRAOIDCodeDate.PutElement( DateImputation, 2);   */

    ASSERT( !_pdmCompta->tblHI->Filtered);

    _pdmCompta->qHI->SQL->Clear();
    _pdmCompta->qHI->SQL->Add("SELECT IDEcriture,CodeJournal FROM REVAO$$H H LEFT OUTER JOIN REVAO$2H H2 ON (H.AutoID=H2.AutoID)");
    _pdmCompta->qHI->SQL->Add(AnsiString("WHERE H.DateImputation = '") + DateImputation.FormatString("mm/dd/yyyy") + "' AND RAOIDLigne = " + RaoIDEcriture + " AND CodeJournal = " + QuotedStr(CodeJournal) );
    _pdmCompta->qHI->Open();

    if(!_pdmCompta->qHI->IsEmpty())
      {// L'écriture à annuler existe
      // => On détruit cette écriture
      sReturn = _pdmCompta->tblHI->AnnulerEcriture( _pdmCompta->qHIIDEcriture->AsInteger, _pdmCompta->tblPC, _pdmCompta->tbl2H) ? CPTA32_YES : CPTA32_NO;
      }
    _pdmCompta->qHI->Close();
    _pdmCompta->qHI->SQL->Clear();


/*    if( _pdmCompta->tblHI->Locate( szfHI_RAOIDLigne ";" szfHI_CodeJournal ";" szfHI_DateImputation, vRAOIDCodeDate, TLocateOptions()))
    { // L'écriture à annuler existe
      // => On détruit cette écriture
      sReturn = _pdmCompta->tblHI->AnnulerEcriture( _pdmCompta->tblHIIDEcriture->AsInteger, _pdmCompta->tblPC, _pdmCompta->tbl2H) ? CPTA32_YES : CPTA32_NO;
    }    */

    CloseTables();
  } catch( Exception& e) {
    if( bOpenned) CloseTables();
    EICDisplayException(tag,e);
    return false;
  }

  return sReturn == CPTA32_YES;
}


bool __fastcall AssEcrAnnule(const AnsiString &CodeJournal,
                             const TDateTime &DateImputation,
                             int RaoIDEcriture,
                             const AnsiString &ComplementPiece)
{
  EICTag tag;
  bool   bOpenned = false;
  short  sReturn = CPTA32_YES;
  char szPiece[ 4]; // N° de pièce de l'écriture
  AnsiString Piece;

  try {
    tag = TAG( 231); OpenTables();
    bOpenned = true;

    szPiece[ 0] = LOBYTE( HIWORD( RaoIDEcriture));
    szPiece[ 1] = HIBYTE( HIWORD( RaoIDEcriture));
    szPiece[ 2] = LOBYTE( RaoIDEcriture);
    szPiece[ 3] = '\0';
    Piece = szPiece + ComplementPiece;
    Variant vRAOIDCodeDate( VarArrayCreate( OPENARRAY( int, (0, 3)), varVariant));

/*    vRAOIDCodeDate.PutElement( RaoIDEcriture,  0);
    vRAOIDCodeDate.PutElement( CodeJournal,    1);
    vRAOIDCodeDate.PutElement( DateImputation, 2);
    vRAOIDCodeDate.PutElement( Piece, 3);  */

    ASSERT( !_pdmCompta->tblHI->Filtered);

    _pdmCompta->qHI->SQL->Clear();
    _pdmCompta->qHI->SQL->Add("SELECT IDEcriture,CodeJournal FROM REVAO$$H H LEFT OUTER JOIN REVAO$2H H2 ON (H.AutoID=H2.AutoID)");
    _pdmCompta->qHI->SQL->Add(AnsiString("WHERE H.DateImputation = '") + DateImputation.FormatString("mm/dd/yyyy") + "' AND RAOIDLigne = " + RaoIDEcriture + " AND CodeJournal = " + QuotedStr(CodeJournal) + " AND Piece = " + QuotedStr(Piece));
    _pdmCompta->qHI->Open();

    if(!_pdmCompta->qHI->IsEmpty())
      {// L'écriture à annuler existe
      // => On détruit cette écriture
      sReturn = _pdmCompta->tblHI->AnnulerEcriture( _pdmCompta->qHIIDEcriture->AsInteger, _pdmCompta->tblPC, _pdmCompta->tbl2H) ? CPTA32_YES : CPTA32_NO;
      }
    _pdmCompta->qHI->Close();
    _pdmCompta->qHI->SQL->Clear();


/*    if( _pdmCompta->tblHI->Locate( szfHI_RAOIDLigne ";" szfHI_CodeJournal ";" szfHI_DateImputation ";" szfHI_Piece, vRAOIDCodeDate, TLocateOptions()))
    { // L'écriture à annuler existe
      // => On détruit cette écriture
      sReturn = _pdmCompta->tblHI->AnnulerEcriture( _pdmCompta->tblHIIDEcriture->AsInteger, _pdmCompta->tblPC, _pdmCompta->tbl2H) ? CPTA32_YES : CPTA32_NO;
    }   */

    CloseTables();
  } catch( Exception& e) {
    if( bOpenned) CloseTables();
    EICDisplayException(tag,e);
    return false;
  }

  return sReturn == CPTA32_YES;
}


 /*############################################################################*\
 ############# EmulEcrValide() ##################################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - permet de valider  #
 # l'écriture (contenue dans la table temporaire du HI) dans le HI du dossier.  #
 \*############################################################################*/
 /*###############################################################*\
 # Cette fonction ne doit être appelée que par les assistants C.   #
 # Les assistants C++ doivents appeler la fonction AssEcrValide.   #
 \*###############################################################*/
DLLCOMPTAAPI(short) EmulEcrValide()
 {
  try {
    if (AssEcrValide())
      return CPTA32_YES;
    return CPTA32_NO;
  } catch( Exception& e) {
    return DllComptaAPIFailed( TAG( 240), e);
  }
 } /* EmulEcrValide() */

bool __fastcall AssEcrValide()
{
  ASSERT_HI();
  ASSERT( _pdmCompta->tblJnx->Exist( _strEcrJournal));

  try {
    if( _pdmCompta->tblHITmp->IsEmpty())
    { // L'écriture à valider est vide
      // => Fermeture des tables de compta
      CloseTables(); // Les tables ont été ouvertes à l'appel de EcritureOuvrir()

      // => Annulation de l'écriture
      return AssEcrAnnule( _strEcrJournal, _dtImputation, _eRaoIDEcriture);
    }
    else
    { // Vérifie que les comptes utilisés dans l'écriture sont compris dans
      // la fourchette 1 à 9
      SCAN_TABLE( _pdmCompta->tblHITmp) {
        AnsiString strCompte( _pdmCompta->tblHITmpCompte->AsString);
        if( strCompte.IsEmpty() || ( strCompte[1]<'1') || ( strCompte[1]>'9'))
          throw EUser( TAG( 235), "Le compte '" + strCompte + "' n'est pas compris dans la fourchette de comptes autorisée.");
      }

      // => Validation de l'écriture
      EcritureValider( _nIDEcriture,
                       _bContrepasser ? (enum TTableHI::HIFlags)( TTableHI::HIFLAGS_NORMALE | TTableHI::HIFLAGS_A_CONTREPASSER)
                                      : TTableHI::HIFLAGS_NORMALE,
                       NULL);

      // Fermeture des tables de compta
      CloseTables(); // Les tables ont été ouvertes à l'appel de EcritureOuvrir()
    }
  } catch( Exception& e) {
    CloseTables();
    EICDisplayException(TAG(241),e);
    return false;
  }

  return true;
}



bool __fastcall AssEcrEstPointee(int nIDEcriture)
{
  bool bEstOuvert = false, bEcrEstPointee = false;
  try
  {
    OpenTables();
    bEstOuvert = true;
    if( _pdmCompta->tblHI->IsEmpty() || (nIDEcriture == 0))
         bEcrEstPointee = false;
    else bEcrEstPointee = _pdmCompta->tblHI->EstEcriturePointee(nIDEcriture);
    CloseTables();
  }
  catch( Exception& e)
  {
    if(bEstOuvert)
      CloseTables();
    EICDisplayException(TAG(242),e);
    return false;
  }
  return bEcrEstPointee;
}


/*############################################################################*\
 ############# AssLstDetailEcriture() ###########################################
 ################################################################################
 #                                                                              #
 #    Cette fonction permet d'obtenir la liste des lignes d'une écriture donnée.#
 #                                                                              #
 # Paramètres :                                                                 #
 #   IDEcriture : IDEcriture de l'écriture à détailler                          #
 #   qDetail    : TQuery contenant le détail demandé                            #
 \*############################################################################*/
void __fastcall AssLstDetailEcriture(const int IDEcriture, TQuery* qDetail)
{
  try
  {
    ASSERT(qDetail);
    qDetail->DatabaseName = "EIC_DOSSIER";
    qDetail->SQL->Text = "SELECT H.Compte,H.DateImputation,H.CodeJournal,H.DateEP,H.EstCrediteur,H.Montant"
                        " FROM REVAO$$H H LEFT OUTER JOIN REVAO$2H H2 ON (H.AutoID=H2.AutoID)"
                        " WHERE H.IDEcriture="+IntToStr(IDEcriture);
  }
  catch (Exception &e)
  {
    EICDisplayException(TAGL,e);
  }
}

/*############################################################################*\
 ############# AssPointerLigneEcriture() ########################################
 ################################################################################
 #                                                                              #
 #    Cette fonction permet de forcer le pointage d'une écriture modifiée.      #
 #                                                                              #
 # Paramètres :                                                                 #
 #   IDEcriture     : IDEcriture de l'écriture à modifier                       #
 #   asNumCompte    : Numéro de compte des lignes d'écriture à modifier         #
 #   dateImputation : Date d'imputation des lignes d'écriture à modifier        #
 #   asCodeJnl      : Code journal des lignes d'écriture à modifier             #
 #   dateEP         : Date de l'état de rapproch. des lignes d'écr. à modifier  #
 #   bEstCrediteur  : Sens du montant des lignes d'écriture à modifier          #
 #   dMontant       : Montant des lignes d'écriture à modifier                  #
 \*############################################################################*/
bool __fastcall AssPointerLigneEcriture(const int IDEcriture,
                                        const AnsiString &asNumCompte,
                                        const TDateTime &dateImputation,
                                        const AnsiString &asCodeJnl,
                                        const TDateTime &dateEP,
                                        bool bEstCrediteur,
                                        double dMontant)
{
  TQuery *q = NULL;
  bool    bEcrRePointee = false, btblHiOuverte = _pdmCompta->tblHI->Active;
  try
  {
    // La table du HI ne doit pas être ouverte durant la maj, si c'est le cas nous la fermons
    if(btblHiOuverte)
      _pdmCompta->tblHI->Close();
    q = new TQuery(NULL);
    q->DatabaseName = "EIC_DOSSIER";
    q->SQL->Text = "UPDATE REVAO$$H "
                  " SET DateEP="+QuotedStr(dateEP.FormatString("mm/dd/yyyy"))+
                  " WHERE AutoID IN (SELECT H.AutoID FROM REVAO$$H H LEFT OUTER JOIN REVAO$2H H2 ON (H.AutoID=H2.AutoID)"
                                   " WHERE (H.IDEcriture="+IntToStr(IDEcriture)+")"
                                   " AND (H.Compte="+QuotedStr(asNumCompte)+")"
                                   " AND (H.DateImputation="+QuotedStr(dateImputation.FormatString("mm/dd/yyyy"))+")"
                                   " AND (H.CodeJournal="+QuotedStr(asCodeJnl)+")"
                                   " AND (H.EstCrediteur="+(bEstCrediteur?"'True'":"'False'")+")"
                                   " AND (H.Montant="+FloatToStr(dMontant)+"))";
    q->ExecSQL();
    bEcrRePointee = (bool)q->RowsAffected;
    // Si la table du HI était ouverte avant la maj, nous la rouvrons
    if(btblHiOuverte)
      _pdmCompta->tblHI->Open();
  }
  catch (Exception &e)
  {
    // Si la table du HI était ouverte avant la maj, nous la rouvrons
    if(btblHiOuverte)
      _pdmCompta->tblHI->Open();
    EICDisplayException(TAGL,e);
  }
  if(q) delete q;
  return bEcrRePointee;
}

 /*############################################################################*\
 ############# PCGetNbEnreg() ###################################################
 ################################################################################
 #                                                                              #
 #    Cette fonction - exportée pour les assistants émulés - retourne le        #
 # nombre total de comptes (comptes centralisateurs, inactifs et supprimés      #
 # comprise) du Plan Comptable.                                                 #
 #                                                                              #
 # Retour :                                                                     #
 #   Le nombre de comptes du PC (100000 si une erreur survient).                #
 \*############################################################################*/
 DLLCOMPTAAPI( long) PCGetNbEnreg()
 {
  int nCount;

  ASSERT_PC();
  ASSERT( !_pdmCompta->tblPC->Filtered);

  // On retourne le Nb max d'enreg de la table pour ne pas planter la barre d'avancement
  try {
    Dbtables::Check( Bde::DbiGetRecordCount( _pdmCompta->tblPC->Handle, nCount));
  } catch( Exception& e) {
    DllComptaAPIFailed( TAG( 250), e);
    nCount = 100000L;
  }
  return nCount;
 } /* PCGetNbEnreg() */

DLLCOMPTAAPI(short) CptaIsPresente( )
{
  return CptaIsPresenteSaufPiece(NULL);
}

DLLCOMPTAAPI(short) CptaIsPresenteSaufPiece( const char FAR* pcszPiece)
{
    CPTA_HI         CptaHI;
    char szJnlODR[13+1], szJnlINVR[13+1], szJnlINVRC[13+1], szJnlCONT[13+1];
    CptaGetJournaux( szJnlODR, sizeof( szJnlODR), szJnlINVR, sizeof( szJnlINVR), szJnlINVRC, sizeof( szJnlINVRC), szJnlCONT, sizeof( szJnlCONT));
    try {
        OpenTables();
    } catch( Exception& e) {
        return false;
    }
    if ( HIGetFirstOfRacine( "", &CptaHI) == CPTA32_YES)
        do
        {
            if( (CptaHI.eTypeJournal != CPTA_JNAL_A_NOUVEAU) &&
                strcmp( CptaHI.szCodeJournal, szJnlODR) &&
                strcmp( CptaHI.szCodeJournal, szJnlINVR) &&
                strcmp( CptaHI.szCodeJournal, szJnlCONT) &&
                !(( strcmp( CptaHI.szRAOPrefixeModule, "EC")==0) && ( CptaHI.cRAOIDEcriture=='S')))
            {
              if (pcszPiece && (AnsiString(CptaHI.szNoPiece).SubString(1,StrLen(pcszPiece)) == pcszPiece))
                continue;
              CloseTables();
              return true;
            }
        }while ( HIGetNextOfRacine( &CptaHI) == CPTA32_YES);
    CloseTables();
    return false;

}

DLLCOMPTAAPI(short) PcAAuMoinsUnCompte( )
{
  bool PCOuvert = false,AuMoinsUnCompte = false;
  try
    {
      ASSERT(_pdmCompta);

      if (!_pdmCompta->tblPC->Exists)
        return false;
      PCOuvert = _pdmCompta->tblPC->Active;
      if (!PCOuvert)
        _pdmCompta->tblPC->Open();
      SCAN_TABLE(_pdmCompta->tblPC)
        if (_pdmCompta->tblPC->EstUnCompte)
          {
            AuMoinsUnCompte = true;
            break;
          }
      if (!PCOuvert)
        _pdmCompta->tblPC->Close();
      return AuMoinsUnCompte;
    }
  catch (Exception &e)
    {
      EICDisplayException(TAGL,e);
      if (!PCOuvert)
        _pdmCompta->tblPC->Close();
      throw e;
    }
}

//#endif // #ifndef USEPACKAGES

